/*
 * CPersonFlowCounter.cpp
 *
 *  Created on: 2018年10月3日
 *      Author: zhengboyuan
 */
#include "stdafx.h"
#include "CPersonFlowCounter.h"
#include "IniFile.h"
#include "TStringUtil.h"

#include "CCameraDevice.h"
#include "DemoCameraDevice.h"

#include "HCNetSDK.h"
#include "CLog.h"
#include "CDataStore.h"


void CPersonFlowCounter::init()
{
	NET_DVR_Init();

	CLog::setLogger(CLog::DEBUGWINDOW);
	CLog::setFileParam("PersonCounter.log", 1024 * 1024 * 4, 3);
	CLog::setLogger(CLog::FILE);

	CLog::info("CPersonFlowCounter::init\n");
}

void CPersonFlowCounter::quit()
{
	NET_DVR_Cleanup();
}


void CALLBACK CPersonFlowCounter::ExceptionCallBack(DWORD dwType, LONG lUserID, LONG lHandle, void *pUser)
{
	CPersonFlowCounter* pthis = (CPersonFlowCounter*)pUser;
	pthis->exceptionCallBack(dwType, lUserID, lHandle);
}



CPersonFlowCounter::CPersonFlowCounter():
    m_sink(),
    m_dataStore(new CDataStore())
{

	BOOL ret = NET_DVR_SetExceptionCallBack_V30(0, NULL, ExceptionCallBack, this);
    
}

CPersonFlowCounter::~CPersonFlowCounter()
{
    close();
}

bool CPersonFlowCounter::open(const char* filepath)
{
    m_dataStore->open("datastore.s3db");

    m_filepath = filepath;

    loadDevices();

    return true;
}

void CPersonFlowCounter::close()
{
	std::string  id;
    CameraDevicePtr device;
    while (m_deviceMap.pop_front(id, device))
    {
        device->close();
    }

    m_dataStore->close();
}

bool CPersonFlowCounter::addCamera(const Camera& camera)
{
	doAddCamera(camera);

    saveDevices();

    return true;
}

bool CPersonFlowCounter::deleteCamera(const std::string& id)
{
    CameraDevicePtr device;
    if (!m_deviceMap.remove(id, device))
    {
        return false;
    }

    device->close();

    saveDevices();

    return true;
}

void CPersonFlowCounter::deleteCameras()
{
	std::string id ;
    CameraDevicePtr device;
    while (m_deviceMap.pop_front(id, device))
    {
        device->close();
    }

    saveDevices();
}

bool CPersonFlowCounter::getCamera(const std::string& id, Camera& camera)
{
	CameraDevicePtr device;
	m_deviceMap.find(id, device);
	if (!device)
	{
		return false;
	}

	return device->getCamera(camera);
}

bool CPersonFlowCounter::isConnected(const std::string& id)
{
	CameraDevicePtr device;
	m_deviceMap.find(id, device);
	if (!device)
	{
		return false;
	}

	return device->isConnected();
}

void CPersonFlowCounter::setSink(PersonFlowSink* sink)
{
    m_sink = sink;
}

bool CPersonFlowCounter::getCount(const std::string& id, PersonFlowCount& flowCount)
{
    CameraDevicePtr device;
    if (!m_deviceMap.find(id, device))
    {
        return false;
    }

    return device->getCount(flowCount);
}

void CPersonFlowCounter::getTotalCount(PersonFlowCount& flowCount)
{
    auto func = [&](const std::string& id, CameraDevicePtr& device) -> bool
    {
        PersonFlowCount deviceFlowCount;
        device->getCount(deviceFlowCount);

        flowCount.enterNum += deviceFlowCount.enterNum;
        flowCount.leaveNum += deviceFlowCount.leaveNum;

        return true;
    };
    m_deviceMap.forEach(func);
}

int CPersonFlowCounter::getCameraCount()
{
	return (int)m_deviceMap.size();
}

bool CPersonFlowCounter::getCamera(int index, Camera& camera)
{
	std::string id;
	CameraDevicePtr device;
	if (!m_deviceMap.getAt(index, id, device))
	{
		return false;
	}

	return device->getCamera(camera);
}

bool CPersonFlowCounter::reconnect(const std::string& id)
{
	CameraDevicePtr device;
	if (!m_deviceMap.find(id, device))
	{
		return true;
	}

	return device->reconnect();
}

void CPersonFlowCounter::onFlowCount(const std::string& id, const PersonFlowCount& flowCount)
{
    saveFlow(id, flowCount);

    if (!m_sink)
    {
        return ;
    }

    m_sink->onFlowCount(id, flowCount);
}

void CPersonFlowCounter::onDeviceStatus(const std::string& id, int connected)
{
    if (!m_sink)
    {
        return ;
    }

    m_sink->onDeviceStatus(id, connected);
}

bool CPersonFlowCounter::saveDevices()
{
	std::vector< CameraDevicePtr > devices;
	m_deviceMap.values(devices);

	comn::IniFile iniFile;

	for (size_t i = 0; i < devices.size(); i++)
	{
		Camera camera;
		devices[i]->getCamera(camera);

		std::string section = "Camera_";
		section += camera.id;

		iniFile.setValue(section.c_str(), "Name", camera.name);
		iniFile.setValue(section.c_str(), "IP", camera.ip);
		iniFile.setValue(section.c_str(), "Port", camera.port);
		iniFile.setValue(section.c_str(), "User", camera.user);
		iniFile.setValue(section.c_str(), "Password", camera.password);
		
	}

	return iniFile.save(m_filepath.c_str());
}

bool CPersonFlowCounter::loadDevices()
{
	comn::IniFile iniFile;
	if (!iniFile.load(m_filepath.c_str()))
	{
		return false;
	}

	size_t count = iniFile.getSectionCount();
	for (size_t i = 0; i < count; i++)
	{
		comn::IniFile::IniSection& section = iniFile.getSection(i);
		if (!comn::StringUtil::startsWith(section.name, "Camera_"))
		{
			continue;
		}

		std::string prefix;
		std::string id;
		comn::StringUtil::split(section.name, '_', prefix, id);

		if (id.empty())
		{
			continue;
		}

		Camera camera;
		camera.id = id;
		section.get("Name", camera.name);
		section.get("IP", camera.ip);
		section.get("Port", camera.port);
		section.get("User", camera.user);
		section.get("Password", camera.password);
		
		doAddCamera(camera);
	}

	return true;
}

bool CPersonFlowCounter::doAddCamera(const Camera& camera)
{
	if (m_deviceMap.exist(camera.id))
	{
		return false;
	}

    //CameraDevicePtr device(new DemoCameraDevice());
    CameraDevicePtr device(new CCameraDevice());
	
	device->open(camera);
	device->setSink(this);

    PersonFlowCount flowCount;
    if (m_dataStore->getFlowCount(camera.id, flowCount))
    {
        device->setCount(flowCount);
    }

	m_deviceMap.put(camera.id, device);

	return true;
}

void CPersonFlowCounter::exceptionCallBack(DWORD dwType, LONG lUserID, LONG lHandle)
{
	TRACE("exceptionCallBack. dwType: 0x%04x, user: %d, handle: %d\n", dwType, lUserID, lHandle);

	if (dwType == EXCEPTION_RELOGIN || dwType == RELOGIN_SUCCESS || dwType == EXCEPTION_RELOGIN_FAILED || dwType == EXCEPTION_EXCHANGE)
	{
		bool connected = (dwType == RELOGIN_SUCCESS);
		CameraDevicePtr device;
		if (findDevice(lUserID, device))
		{
			device->setConnected(connected);
		}
	}
}

bool CPersonFlowCounter::findDevice(LONG userID, CameraDevicePtr& device)
{
	auto pred = [&](const std::string& id, CameraDevicePtr& cameraDevice) -> bool
	{
		CCameraDevice* dev = dynamic_cast<CCameraDevice*>(cameraDevice.get());
		if (!dev)
		{
			return false;
		}

		return dev->getLoginUser() == userID;
	};

	std::string id;
	return m_deviceMap.findIf(pred, id, device);
}

void CPersonFlowCounter::saveFlow(const std::string& id, const PersonFlowCount& flowCount)
{
    m_dataStore->setFlowCount(id, flowCount);
}